﻿// Copyright 2009 Mike Geise
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Linq;
using Falcon.Core.Collections;
using Falcon.Core.Domain;
using Falcon.Core.Domain.Enums;
using Falcon.Core.Domain.Repositories;
using Falcon.Core.Domain.Services;
using Falcon.Core.Domain.Values;
using Falcon.Core.Email;
using Falcon.Core.Extensions;

namespace Falcon.Core.Services
{
	public class UserMessageService : IUserMessageService
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="UserMessageRepository"/> class.
		/// </summary>
		/// <param name="identityService">The identity service.</param>
		/// <param name="emailService">The email service.</param>
		/// <param name="userService">The user service.</param>
		/// <param name="userPreferenceService">The user preference service.</param>
		/// <param name="userMessageAttachmentService">The user message attachment service.</param>
		/// <param name="userMessageRepository">The user message repository.</param>
		public UserMessageService(
			IIdentityService identityService,
			IEmailService emailService,
			IUserService userService,
			IUserPreferenceService userPreferenceService,
			IUserMessageAttachmentService userMessageAttachmentService,
			IUserMessageRepository userMessageRepository)
		{
			this.IdentityService = identityService;
			this.EmailService = emailService;
			this.UserService = userService;
			this.UserPreferenceService = userPreferenceService;
			this.UserMessageAttachmentService = userMessageAttachmentService;
			this.UserMessageRepository = userMessageRepository;
		}

		/// <summary>
		/// Gets or sets the identity service.
		/// </summary>
		/// <value>The identity service.</value>
		public IIdentityService IdentityService
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets or sets the email service.
		/// </summary>
		/// <value>The email service.</value>
		public IEmailService EmailService
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets or sets the user service.
		/// </summary>
		/// <value>The user service.</value>
		public IUserService UserService
		{
			get; 
			private set;
		}

		/// <summary>
		/// Gets or sets the user preference service.
		/// </summary>
		/// <value>The user preference service.</value>
		public IUserPreferenceService UserPreferenceService
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets or sets the user message attachment service.
		/// </summary>
		/// <value>The user message attachment service.</value>
		public IUserMessageAttachmentService UserMessageAttachmentService
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets or sets the user message repository.
		/// </summary>
		/// <value>The user message repository.</value>
		public IUserMessageRepository UserMessageRepository
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets the user message by the specified specification.
		/// </summary>
		/// <param name="specification">The <see cref="UserMessageSpecification"/>.</param>
		/// <returns></returns>
		public UserMessage GetBy(UserMessageSpecification specification)
		{
			// get message
			var message = this.UserMessageRepository.GetBy(specification);

			// get attachments for message
			message.Attachments = this.UserMessageAttachmentService.GetAll(new UserMessageAttachmentSpecification
			{
				MessageId = message.Id
			}).Select(x => x.Attachment).ToList();

			return message;
		}

		/// <summary>
		/// Gets the user message by id.
		/// </summary>
		/// <param name="id">The id.</param>
		/// <returns></returns>
		public UserMessage GetById(int id)
		{
			// get message
			var message = this.UserMessageRepository.GetById(id);

			// get attachments for message
			message.Attachments = this.UserMessageAttachmentService.GetAll(new UserMessageAttachmentSpecification
			{
				MessageId = message.Id
			}).Select(x => x.Attachment).ToList();

			return message;
		}

		/// <summary>
		/// Gets all the user messages.
		/// </summary>
		/// <returns></returns>
		public IList<UserMessage> GetAll()
		{
			return this.UserMessageRepository.GetAll();
		}

		/// <summary>
		/// Gets all the user messages.
		/// </summary>
		/// <param name="specification">The <see cref="UserMessageSpecification"/>.</param>
		/// <returns></returns>
		public IPagedList<UserMessage, UserMessageSpecification> GetPaged(UserMessageSpecification specification)
		{
			return this.UserMessageRepository.GetPaged(specification);
		}

		/// <summary>
		/// Creates the user message.
		/// </summary>
		/// <returns></returns>
		public UserMessage Create()
		{
			var message = new UserMessage
			{
				SenderActive = true,
				ReceiverActive = true,
				ReceiverRead = false,
				ReceiverReplied = false,
				ReceiverForwarded = false,
				Created = DateTime.Now
			};

			message.Updated = message.Created;

			return message;
		}

		/// <summary>
		/// Gets the count of user messages using the specified specification.
		/// </summary>
		/// <param name="specification">The specification.</param>
		/// <returns></returns>
		public int Count(UserMessageSpecification specification)
		{
			return this.UserMessageRepository.Count(specification);
		}

		/// <summary>
		/// Marks the specified message as read.
		/// </summary>
		/// <param name="message">The message.</param>
		public void MarkRead(UserMessage message)
		{
			if (!message.IsReceiver || message.ReceiverRead || message.ReceiverReplied || message.ReceiverForwarded)
			{
				return;
			}

			message.ReceiverRead = true;
			message.Read = DateTime.Now;

			// update message
			this.Update(message);

			// update user preference (messages)
			this.UpdatePreference(message, false);

			// refresh user session
			Application.Session(true);
		}

		/// <summary>
		/// Moves the specified user messages.
		/// </summary>
		/// <param name="values">The values.</param>
		/// <param name="folder">The folder.</param>
		public void Move(UserMessageMove[] values, UserMessageFolder folder)
		{
			if (values == null)
			{
				return;
			}

			for (var i = 0; i < values.Length; i++)
			{
				var message = this.GetById(values[i].Id);

				if (message == null || !message.CanUpdate || !message.IsReceiver)
				{
					continue;
				}

				message.Folder = folder.Title == UserMessageFolder.Inbox ? null : folder;

				this.Update(message);
			}
		}

		/// <summary>
		/// Inserts the user message into the storage.
		/// </summary>
		/// <param name="message">The <see cref="UserMessage"/>.</param>
		public void Insert(UserMessage message)
		{
			message.Validate();

			message.Slug = message.Title.Slug();
			message.BodyParsed = message.Body.BBCodeToHtml(true);

			this.UserMessageRepository.Insert(message);
		}

		/// <summary>
		/// Inserts the user message into the storage.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="value">The value.</param>
		public void Insert(UserMessage message, UserMessageCreate value)
		{
			value.Validate();

			var identity = IdentityService.Current;

			var receiver = this.UserService.GetBy(new UserSpecification
			{
				Name = value.Name
			});

			if (receiver == null)
			{
				return;
			}

			if (!value.Forwarded)
			{
				message.ParentId = value.ParentId;
			}

			message.Title = value.Title;
			message.Body = value.Body;
			message.Sender = this.UserService.GetById(identity.Id);
			message.Receiver = receiver;

			this.Insert(message);
			this.UpdatePreference(message, true);
			this.UserMessageAttachmentService.Insert(message, value.Attachments);

			if (value.ParentId > 0)
			{
				var parent = this.GetById(value.ParentId);

				if (value.Replied)
				{
					parent.ReceiverReplied = true;
				}
				else if (value.Forwarded)
				{
					parent.ReceiverForwarded = true;
				}

				this.Update(parent);
			}

			if (!receiver.Preference.IsMessagingNotificationEnabled)
			{
				return;
			}

			this.EmailService.Send(new UserMessageEmail
			{
				To = receiver.EmailAddress,
				Message = message
			});
		}

		/// <summary>
		/// Updates the specified user message in the storage.
		/// </summary>
		/// <param name="message">The <see cref="UserMessage"/>.</param>
		public void Update(UserMessage message)
		{
			message.Slug = message.Title.Slug();
			message.BodyParsed = message.Body.BBCodeToHtml(true);
			message.Updated = DateTime.Now;

			this.UserMessageRepository.Update(message);
		}

		/// <summary>
		/// Updates the specified user messages in the storage.
		/// </summary>
		/// <param name="messages">The messages.</param>
		/// <param name="action">The action.</param>
		public void Update(UserMessageUpdate[] messages, UserMessageAction action)
		{
			for (var i = 0; i < messages.Length; i++)
			{
				if (!messages[i].Selected)
				{
					continue;
				}

				var message = this.GetById(messages[i].Id);

				if (action == UserMessageAction.Delete)
				{
					if (message.IsReceiver)
					{
						message.ReceiverActive = false;
					}
					else
					{
						message.SenderActive = false;
					}

					this.Delete(message);
				}
				else
				{
					message.ReceiverRead = (action == UserMessageAction.MarkRead)
						? true
						: false;

					this.Update(message);
				}
			}
		}

		/// <summary>
		/// Deletes the specified user message in the storage.
		/// </summary>
		/// <param name="message">The <see cref="UserMessage"/>.</param>
		public void Delete(UserMessage message)
		{
			if (!message.SenderActive && !message.ReceiverActive)
			{
				this.UserMessageRepository.Delete(message);
			}
			else
			{
				this.Update(message);
			}
		}

		/// <summary>
		/// Updates the user preference.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="addition">if set to <c>true</c> [addition].</param>
		private void UpdatePreference(UserMessage message, bool addition)
		{
			var preference = this.UserPreferenceService.GetBy(new UserPreferenceSpecification
			{
				UserId = message.ReceiverId
			});

			if (preference == null)
			{
				return;
			}

			preference.UpdateMessages(1, addition);

			this.UserPreferenceService.Update(preference);
		}
	}
}